package squares;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import conditions.SearchCondition;


import dungeons.Dungeon;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Model;
import be.kuleuven.cs.som.annotate.Raw;
import borders.*;
/**
 * A class to construct and modify a square with its attributes temperature, humidity, slipperyFloor and borders
 * involving the different temperature types, rust damage, heat and cold damage, slippery, inhabitability.
 * A square is the smallest unit of location and is able to merge with other squares.
 * 
 * @invar	the temperature of this square must be valid
 * 			| isValidTemperature(getTemperature())
 * @invar   the humidity of this square must be valid
 * 			| isValidHumidity(getHumidity())
 * @invar	the square must be initialized with at the 6 directions no null borders or the hashmap has to be completely empty
 * 			| getBorders().size() == 6
 * 			| && for(Direction direction: Direction.values())
 * 			| 		this.getBorderAt(direction) != null
 * 			| 		|| borders.isEmpty()
 * @invar 	The borders of this square are all valid.
 * 			| for( Direction direction: Direction.values())
 * 			|  	this.isValidBorder(direction,this.getBorderAt(direction))
 * @invar	the temperaturestep for the heat damage must be valid.
 * 			| getTemperatureStepHeatDamage() >= 0
 * @version 2.0
 * @author 	Gilles Groven & Jochen Tombal
 *
 */
public class Square implements SearchCondition {
	
	
	protected double temperature;
	protected double humidity;
	private boolean slipperyFloor;
	protected HashMap< Direction,Border> borders; 
	private static double MIN_TEMPERATURE = -200;
	private static double MAX_TEMPERATURE = 5000;
	private static double startTemperatureHeatDamage = 35;
	private static double temperatureStepHeatDamage = 15;
	protected Teleportation teleport;
	private boolean isTerminated = false;
	
	/**
	 * temperatureType contains parameters to distinguish between types of temperature
	 */
	public enum TemperatureType {FAHRENHEIT,KELVIN,CELSIUS};
	public static double temperatureMergeConstant = 0.2;
	
	/**
	 * initializes this square with the given temperature and humidity, slippery of the floor and initializes its borders,and the possibility to teleport.
	 * 
	 * @param temperature
	 * 			the temperature you want to give to this square
	 * @param type
	 * 			the type of the temperature (fahrenheit,kelvin or celsius)
	 * @param humidity
	 * 			the humidity of this square, going from 0 to 100	
	 * @param slipperyFloor
	 * 			indicates if the floor is slippery or not.
	 * @param teleportPossible
	 * 			indicates if this square can teleport or not.
	 * @post
	 * 		the given temperature is equal to the temperature of this square in the unit of the given temperature.
	 * 		| new.getTemperature(type) == temperature
	 * @post
	 * 		the given humidity is equal to the humidity of this square.
	 * 		| new.getHumidity() == humidity
	 * @post
	 * 		the given boolean is equal to the attribute slippery of the floor of this square.
	 * 		| new.getSlipperyFloor() == slipperyFloor
	 * @post
	 * 		the square has 6 borders and is initialized with all walls
	 * 		| new.getBorders() != null
	 * 		| && new.getBorders().size() == 6
	 * 		| && new.getBorderAt(Direction.values()).getSlipperyWall() == false
	 * 		| && new.getBorderAt(Direction.values()) instanceof Wall
	 * @throws InvalidTemperatureException
	 * 		The given temperature is not valid for this square
	 * 		| !IsValidTemperature(temperature)
	 * @throws InvalidHumidityException
	 * 		The given humidity is not valid for this square
	 * 		| !isValidHumidity(Humidity)
	 * @throws IllegalTemperatureTypeException
	 * 		The given TemperatureType is not valid
	 * 		| type == null
	 */
	public Square(double temperature,TemperatureType type,double humidity, boolean slipperyFloor,boolean teleportPossible) throws InvalidTemperatureException,InvalidHumidityException,IllegalTemperatureTypeException
	{
		assert hasTwoDecimals(humidity);
		if(type == null)
			throw new IllegalTemperatureTypeException("invalid TemperatureType");
		if(!isValidTemperature(temperature,type))
			throw new InvalidTemperatureException(temperature);
		if(!isValidHumidity(humidity))
			throw new InvalidHumidityException(humidity);
		this.borders = new HashMap<Direction,Border>();
		makeBorders();
		this.setTemperatureToCelsius(type,temperature);
		this.humidity = humidity;
		this.slipperyFloor = slipperyFloor;
		this.teleport = new Teleportation(teleportPossible);
		
		
	}
	/**
	 * initializes this square with the given temperature and humidity, slippery of the floor and lets the user decides which borders he wants to use,and the possibility to teleport.
	 * 
	 * @param 	temperature
	 * 			the temperature you want to give to this square
	 * @param	type
	 * 			the type of the temperature (fahrenheit,kelvin or celsius)
	 * @param 	humidity
	 * 			the humidity of this square, going from 0 to 100	
	 * @param 	slipperyFloor
	 * 			indicates if the floor is slippery or not.
	 * @param 	north
	 * 			the northern border of the square.
	 * @param 	east
	 * 			the eastern border of the square.
	 * @param 	south
	 * 			the southern border of the square.
	 * @param 	west
	 * 			the western border of the square.
	 * @param 	ceiling
	 * 			the border kind of the ceiling.
	 * @param 	floor
	 * 			the border kind of the floor (can not be a door).
	 * @param 	teleportPossible
	 * 			indicates if teleportation is possible for this square.
	 * @post
	 * 			the given temperature is equal to the temperature of this square in the unit of the given temperature.
	 * 			| new.getTemperature(type) == temperature
	 * @post
	 * 			the given humidity is equal to the humidity of this square.
	 * 			| new.getHumidity() == humidity
	 * @post
	 * 			the given boolean is equal to the attribute slippery of the floor of this square.
	 * 			| new.getSlipperyFloor() == slipperyFloor
	 * @post
	 * 			the square has 6 borders and is initialized
	 * 			| new.getBorders() != null && new.getBorders().size() == 6;
	 * @post
	 * 			the teleport is possible for this square if stated so
	 * 			| if(teleportPossible)
	 * 			|	then teleportation.teleportPossibe() == true
	 * @post
	 * 			if the given border was a valid border for the direction, the direction has been changed, otherwise it is a non slippery wall.
	 * 			|if(isValidBorder(north))
	 * 			| 	then new.getBorderAt(Direction.NORTH) == north
	 *  		|else if(isValidBorder(east))
	 * 			| 	then new.getBorderAt(Direction.EAST) == east
	 *  		|else if(isValidBorder(south))
	 * 			| 	then new.getBorderAt(Direction.SOUTH) == south
	 *  		|else if(isValidBorder(west))
	 * 			| 	then new.getBorderAt(Direction.WEST) == west
	 *  		|else if(isValidBorder(floor))
	 * 			| 	then new.getBorderAt(Direction.FLOOR) == floor
	 *  		|else if(isValidBorder(ceiling))
	 * 			| 	then new.getBorderAt(Direction.CEILING) == ceiling
	 * 			|else
	 * 			|   new.getBorderAt(Direction.values()) instanceof Wall && new.getBorderAt(Direction.values()).getSlipperyWall() == false
	 * @throws 	InvalidTemperatureException
	 * 			The given temperature is not valid for this square
	 * 			| !IsValidTemperature(temperature)
	 * @throws 	InvalidHumidityException
	 * 			The given humidity is not valid for this square
	 * 			| !isValidHumidity(Humidity)
	 * @throws 	IllegalTemperatureTypeException
	 * 			The given TemperatureType is not valid
	 * 			| type == null
	 */
	public Square(double temperature,TemperatureType type,double humidity, boolean slipperyFloor,boolean teleportPossible,Border north,Border east,Border south,Border west,Border floor,Border ceiling) throws InvalidTemperatureException,InvalidHumidityException,IllegalTemperatureTypeException
	{
		assert hasTwoDecimals(humidity);
		if(type == null)
			throw new IllegalTemperatureTypeException("invalid TemperatureType");
		if(!isValidTemperature(temperature,type))
			throw new InvalidTemperatureException(temperature);
		if(!isValidHumidity(humidity))
			throw new InvalidHumidityException(humidity);
		this.borders = new HashMap<Direction,Border>();
		makeBorders();
		setBorder(Direction.NORTH,north);
		setBorder(Direction.EAST,east);
		setBorder(Direction.SOUTH,south);
		setBorder(Direction.WEST,west);
		setBorder(Direction.FLOOR,floor);
		setBorder(Direction.CEILING,ceiling);
		this.setTemperatureToCelsius(type,temperature);
		this.humidity = humidity;
		this.slipperyFloor = slipperyFloor;
		this.teleport = new Teleportation(teleportPossible);
	}
	/**
	 * makes a standard square with initialized variables, and no possibility to teleport.
	 * 
	 * @post
	 * 		 the temperature of this square is zero degrees
	 * 		| new.getTemperature() == 0
	 * @post
	 * 		 the humidity of this square is zero.
	 * 		| new.getHumidity() == 0
	 * @post
	 * 		the slippery of the floor of this square is set to false
	 * 		| new.getSlipperyFloor() == false
	 * @post
	 * 		the square has 6 borders and is initialized
	 * 		| new.getBorders() != null && new.getBorders().size() == 6;
	 */
	public Square()
	{
		this.temperature = 0;
		this.humidity = 0;
		this.borders = new HashMap<Direction,Border>();
		this.slipperyFloor = false;
		makeBorders();
		this.teleport = new Teleportation(false);
	}
	/**
	 * changes the temperature of this square to the mean of the given temperature and the temperature of the squares in the same space.
	 * 
	 * @param temperature
	 * 			The new Temperature in celsius.
	 * @post	the new temperature is valid for this square
	 * 			| this.isValidTemperature(new.getTemperature())
	 * @post	the new temperature is the mean temperature of the given temperature and the temperature of the squares in its space.
	 * 			|if(temperature >= this.getTemperature())
	 * 			| 	then (new.getTemperature() =< temperature
	 * 			|		&& new.getTemperature() >= this.getTemperature())
	 * 			|else(assert(temperature < this.getTemperature())
	 * 			| 	&& new.getTemperature() > temperature 
	 * 			|	&& new.getTemperature() < this.getTemperature())
	 * @post 	each square in this square his space has the same temperature
	 * 			|for(Square square Dungeon.findSpace(this))
	 * 			|  new.getTemperature() == square.new.getTemperature()
	 * @throws InvalidTemperatureException
	 * 			Throws this Exception if the given value is not contained within the minimum and 
	 * 			maximum Temperature of this square
	 * 			| !IsValidTemperature(temperature,TemperatureType.CELSIUS)
	 */
	public void setTemperature(double temperature) throws InvalidTemperatureException
	{
		try{
		if(!isValidTemperature(temperature,TemperatureType.CELSIUS))
			throw new InvalidTemperatureException(temperature);
		else
		{
			this.temperature = temperature;
			adjustTemperatureSpace();
		}
		}
		catch (IllegalTemperatureTypeException exc)
		{
		}
		

	}
	/**
	 * 
	 * changes the temperature of the space where this square is part of
	 * @post	the temperature of each square of the space is equal
	 * 			|for(Square spaceSquare : Dungeon.findSpace(square))
	 * 			|	for (Square otherSpaceSquare : Dungeon.findSpace(square)
	 * 			|		spaceSquare.getTemperature() = otherSpaceSquare.getTemperature()
	 * @post	the temperature of each square of the space is a valid temperature
	 *			|for (Square otherSpaceSquare : Dungeon.findSpace(square)
	 * 			|		IsValidTemperature(spaceSquare.getTemperature(),TemperatureType.CELSIUS)
	 * @post	the temperature of the given square is the mean of the squares in the space or equal to its old value if no other squares are in the space.
	 * 			| if(Dungeon.findSpace(square).size() == 1)
	 * 			|   then(new.getTemperature() == this.getTemperature())
	 * 			| if(square.this.getTemperature() >= Dungeon.findSpace(square).get(1).getTemperature())
	 * 			| 	then ( new.getTemperature() =< this.getTemperature()
	 * 			|			&& new.getTemperature() >= Dungeon.findSpace(square).get(1).getTemperature())
	 * 			|else (assert(square.this.getTemperature() < Dungeon.findSpace(square).get(1).getTemperature())
	 * 			| 		&& new.getTemperature() > temperature
	 * 			|		&& new.getTemperature() < this.getTemperature())
	 */
	public void adjustTemperatureSpace()
	{
		HashMap<String,HashSet<Square>> spaceWithRocks = Dungeon.findSpace(this);
		HashSet<Square> spaceWithoutRocks = spaceWithRocks.get("squares");
		int denominator = spaceWithoutRocks.size();
		double totalTemperature = 0;
		for(Square spaceSquare : spaceWithoutRocks)
		{
			totalTemperature += spaceSquare.getTemperature();
		}
		double temperaturePerSquare = totalTemperature/denominator; 
		for(Square spaceSquare : spaceWithoutRocks)
		{
				spaceSquare.temperature = temperaturePerSquare;
		}
		
		for(Square spaceRockSquare : spaceWithRocks.get("rocks"))
		{
			((RockSquare)spaceRockSquare).adjustTemperature();
		}
		
	}
	
	/**
	 * rounds off the given number up to two fraction numbers
	 * 
	 * @post
	 * 		the returned value must contain 2 fraction numbers
	 * 		
	 * @param number
	 * 		the number to be changed to the two decimals format
	 * @return 
	 * 		the given number as a double with at least one integer digits and with 2
	 * 		Fraction digits.
	 */
	public static boolean hasTwoDecimals(double number)
	{
			Double rounder = Math.abs(number)*100;
			return ((double)rounder.intValue() == Math.abs((number)*100));
	}
	/**
	 * Initializes the borders of this square to a state where it has 6 borders.
	 * 
	 * @post	if borders wasn't initialized, it has been initialized.
	 * 			| new.getBorders() != null;
	 * @post	the HashMap borders of this square have been set with 6 Directions as keys
	 * 			and with corresponding Border.
	 * 			|for(Direction direction : Direction.Values())
	 * 			|	this.getBorderAt(direction != null)
	 * 			
	 * @effect	this method makes sure that when a square is initialized,
	 * 			it will have 6 non slippery walls in all Directions.
	 * 		
	 */
	@Model
	protected void makeBorders()
	{
		if(this.borders == null)
			this.borders = new HashMap<Direction,Border>();
		if( borders.isEmpty())
			borders.put(Direction.NORTH, new Wall(this,false));borders.put(Direction.EAST,new Wall(this,false));borders.put(Direction.WEST,new Wall(this,false));
			borders.put(Direction.SOUTH,new Wall(this,false));borders.put(Direction.CEILING, new Wall(this,false));borders.put(Direction.FLOOR,new Wall(this,false));
	}
		
	/**
	 * changes the border in the given direction, can only be used by the constructor
	 * @param direction
	 * 		the direction in which the border will be set.
	 * @param border
	 * 		the new Border you want to set
	 * @post	the new border in the given direction is equal to the given border, or left unchanged if not valid
	 * 			| if(isValidBorder(direction,border)
	 * 			|	then new.getBorderAt(direction) == border
	 * 			| else (new.getBorderAt(direction) == this.getBorderAt(direction))
	 */
	protected void setBorder(Direction direction,Border border)
	{
		if(isValidBorder(direction,border))
			this.borders.put(direction,border);
	}
	
	/**
	 * returns the border of this square in the given direction.
	 * 
	 * @param direction
	 * 			the direction of the border you want to find.
	 * @return
	 * 			the Border in the given direction.
	 * 			|if (direction == null)
	 * 			| 	then result = null
	 * 			|else  result = borders.get(direction)
	 */
	@Raw
	public Border getBorderAt(Direction direction)
	{
		if(direction == null)
			return null;
		else
			return borders.get(direction);
	}
	/**
	 * Changes the border at a square at a given direction if allowed
	 * 
	 * @param 	direction
	 * 			the direction of the border you want to change.
	 * @param 	border
	 * 			the border that is to replace the current border
	 * @post	the Border at the given direction is a valid border
	 * 			| isValidBorder(this.getBorderAt(direction),direction)
	 * @post	if the space of this square has  been enlarged, the temperature and humidity have been adjusted accordingly
	 * 			|for(Square square : new.Dungeon.findSpace(this))
	 * 			|  this.new.getTemperature() == square.new.getTemperature()
	 * 			|  && this.new.getHumidity() == square.new.getHumidity()		
	 */
	public void changeBorderAt(Direction direction,Border border)
	{
		if(BorderCanBeChanged(direction,border))
			if(border.hasOnlyOneSquareAttached())
			{
				this.borders.put(direction, border);
				
			}
			else
			{
				this.borders.put(direction, border);
				border.getOtherAttachedSquare(this).borders.put(getOppositeDirection(direction), border);
			}
		if(border instanceof Door)
		{
			if(((Door)border).getIsOpen())
				adjustTemperatureSpace();
				adjustHumiditySpace();
		}
		if(border instanceof Open)
		{
			adjustTemperatureSpace();
			adjustHumiditySpace();			
		}
	}
	/**
	 * returns the direction of a given border in this square
	 * 
	 * @param border
	 * 		the border of which the direction is to be inspected
	 * @return
	 * 		the direction of the border in this square
	 * 		|border == this.borders.get(result)
	 */
	@Model
	protected Direction getDirectionOfBorder(Border border)
	{
		Direction dir = null;
		Iterator<Direction> it = this.borders.keySet().iterator();
		while(it.hasNext())
		{
			Direction d = it.next();
			if(borders.get(d).equals(border))
				{
				 	dir = d;
				}
		}
		return dir;
	}
	public boolean isConnectedToOtherSquare()
	{
		for(Border border: borders.values())
		{
			if(!border.hasOnlyOneSquareAttached())
				return true;
				
		}
		return false;
	}
	/**
	 * checks if the square has already a border with the given square in an other direction.
	 * @param direction
	 * 			the direction in which the square wants to make a border
	 * @param square
	 * 			the square to be checked
	 * @return true if a border with the square is already present
	 * 		|for(border Border: borders.values())
	 * 		|	if(border.getAttachedSquare1().equals(square) || border.getAttachedSquare2().equals(square)) && getDirectionOfBorder(border) != direction)
	 * 		|		then  result == true
	 */
	@Model
	protected boolean borderInOtherDirection(Direction direction,Square square)
	{
		if(square == null)
			return false;
		Iterator<Border> it = borders.values().iterator();
		while(it.hasNext())
		{
			Border border = it.next();
			if(border != null)
			{
				if(getDirectionOfBorder(border) != direction)
				{
					if(border.getAttachedSquare1() != null)
					{
						if(border.getAttachedSquare1().equals(square))
							return true;
					}
					else if(border.getAttachedSquare2() != null)
					{
						if(border.getAttachedSquare2().equals(square))
							return true;
					}
				}
			}
		}
		return false;
	}
	/**
	 * returns a boolean if the border in a given direction is allowed to be changed into a given border.
	 * 
	 * @param 	direction
	 * 			the direction of the border that the users checks if replaceable 
	 * @param 	border
	 * 			the border that is to be checked if allowed to replace
	 * @return	true if the border at a given direction is allowed to be changed into a given border
	 * 			|if(border != null && direction != null)
	 * 			|	if(Border connected to this)
	 * 			|		if(((!ToManyDoors() || direction = Direction.FLOOR) && border instanceof Door)
	 * 			|			 &&!ToManyOpenBorders() && border instanceof Open
	 * 			|			 && border != null && direction != null
	 * 			| 			 && !border.hasOnlyOneAttached() )
	 * 			|				and if(border equals border at direction)
	 * 			|					for other attached
	 * 			|						if(((!ToManyDoors() || direction = Direction.FLOOR) && border instanceof Door)
	 * 			|						&& !ToManyOpenBorders() && border instanceof Open)  
	 * 			|						&& border != null && direction != null)
	 * 			|							then (result == true)
	 * 			|			else (result == true)
	 * 			|else(result == false)			
	 */
	public boolean BorderCanBeChanged(Direction direction,Border border)
	{
		if( border == null || direction == null)
			return false;
		if(border.getAttachedSquare1() != null)
		{
			if(!border.getAttachedSquare1().equals(this))
			{
				if(border.getAttachedSquare2() != null)
				{
					if(!border.getAttachedSquare2().equals(this))
					{
						return false;
					}
				}
			}
		}
		
		if(border instanceof Door)
		{
			if(this.toManyDoors(direction))
			{
				return false;
			}
		}
		if(border instanceof Open)
		{
			if(this.toManyOpenBorders(direction))
				return false;
		}
		if(direction == Direction.FLOOR && border instanceof Door)
		{ 
			return false;
		}
		if(!border.hasOnlyOneSquareAttached())
		{
			if(borderInOtherDirection(direction,border.getOtherAttachedSquare(this)))
				return false;
			if((border.getAttachedSquare1().equals(borders.get(direction).getAttachedSquare1()) 
					|| border.getAttachedSquare1().equals(borders.get(direction).getAttachedSquare2()))
					&& (border.getAttachedSquare2().equals(borders.get(direction).getAttachedSquare1()) 
					|| border.getAttachedSquare2().equals(borders.get(direction).getAttachedSquare2())))
			{
				if(isRightDoubleBorderDirection(direction,borders.get(direction).getOtherAttachedSquare(this).getDirectionOfBorder(borders.get(direction))))
						{
						Direction otherDirection = Square.getOppositeDirection(direction);
					if(border instanceof Door)
					{
						if(border.getOtherAttachedSquare(this) instanceof TransparentSquare)
						{
							if(!(border.getOtherAttachedSquare(this).getBorderAt(otherDirection) instanceof Door) && !(border.getOtherAttachedSquare(this).getBorderAt(Square.getOppositeDirection(otherDirection)) instanceof Door))
								return false;
						}
						else if(border.getOtherAttachedSquare(this) instanceof RockSquare)
							return false;
						else
						{
						if(border.getOtherAttachedSquare(this).toManyDoors(otherDirection))
						{
							return false;
						}
						}
					}
					else if(border instanceof Open)
					{
						if(border.getOtherAttachedSquare(this) instanceof RockSquare)
						{
							return false;
						}
						else if(border.getOtherAttachedSquare(this) instanceof TransparentSquare)
						{
							if(!(border.getOtherAttachedSquare(this).getBorderAt(otherDirection) instanceof Open) && !(border.getOtherAttachedSquare(this).getBorderAt(Square.getOppositeDirection(otherDirection)) instanceof Door))
							{
								return false;
							}
						}
						else
						{
							if(border.getOtherAttachedSquare(this).toManyOpenBorders(otherDirection))
							{
								return false;
							}
						}
					}
					else if(border instanceof Wall)
					{
						if(border.getOtherAttachedSquare(this) instanceof TransparentSquare)
							return false;
					}
					if(otherDirection == Direction.FLOOR && border instanceof Door && !(border.getOtherAttachedSquare(this) instanceof TransparentSquare))
					{ 
						return false;
					}
					return true;
						}
				else
					return false;
			}
			else
			{
				return false;
			}
		}
		else {
			if(!borders.get(direction).hasOnlyOneSquareAttached())
				return false;
		}
		return true;
	}
	/**
	 * checks if the two directions are opposite to each other (north <-> south ...)
	 * @param directionThis
	 * 		the first direction
	 * @param directionOther
	 * 		the second direction
	 * @return true if the directions are opposite
	 * 		|if((directionThis==Direction.CEILING && directionOther == Direction.FLOOR)
	 * 		|	||(directionThis== Direction.FLOOR && directionOther==Direction.CEILING) 
	 *		|	||(directionThis== Direction.NORTH && directionOther== Direction.SOUTH)
	 *		|	||(directionThis== Direction.EAST && directionOther== Direction.WEST)
	 *		|	||(directionThis==Direction.SOUTH && directionOther==Direction.NORTH)
	 *		|	||(directionThis==Direction.WEST && directionOther==Direction.EAST))
	 *		|		then(result == true)
	 */
	protected static boolean isRightDoubleBorderDirection(Direction directionThis,Direction directionOther)
	{
		if((directionThis==Direction.CEILING && directionOther == Direction.FLOOR) ||(directionThis== Direction.FLOOR && directionOther==Direction.CEILING) 
				||(directionThis== Direction.NORTH && directionOther== Direction.SOUTH) ||(directionThis== Direction.EAST && directionOther== Direction.WEST)
				||(directionThis==Direction.SOUTH && directionOther==Direction.NORTH) ||(directionThis==Direction.WEST && directionOther==Direction.EAST))
		{
			return true;
		}
		else
			return false;
		
	}
	/**
	 * checks if the border is valid in general and in the given direction, this method checks for new borders, not for merged borders
	 * @param direction
	 * 			the direction you want to put the border.
	 * @param border
	 * 			the border you want to set.
	 * @return true if the border is a door and the square has not to many doors,its direction is not the floor and the square doesn't have to many open borders to make a new open border.
	 * 			| if( border != null && direction != null && (border.getAttachedSquare1().equals(this) && border.getAttachedSquare2().equals(this))
	 * 			|	and if((border instanceof Door && !toManyDoors()) || (border instanceof Open && !toManyOpenBorders()))
	 * 			|		 then(result == true)
	 */
	@Model
	protected boolean isValidBorder(Direction direction,Border border)
	{
		if( border == null || direction == null)
			return false;
		if(!border.hasOnlyOneSquareAttached())
			return false;
		if(border.getAttachedSquare1() != null)
		{
			if(!border.getAttachedSquare1().equals(this))
			{
				if(border.getAttachedSquare2() != null)
				{
					if(!border.getAttachedSquare2().equals(this))
					{
						return false;
					}
				}
			}
		}

		if(border instanceof Door)
		{
			if(toManyDoors(direction))
			{
				return false;
			}
			else
				return true;
		}
		if(border instanceof Open)
		{
			if(toManyOpenBorders(direction))
				return false;
		}
		if(direction == Direction.FLOOR && border instanceof Door)
		{ 
			return false;
		}
		return true;
	}
	/**
	 * method to check if the square has more 3 or more doors.
	 * @return true if the square 3 as borders.
	 * 		| if( borders.values contains 3>= instanceof Door)
	 * 		| 	then(result == true)
	 */
	public boolean toManyDoors(Direction direction)
	{
		int i = 0;
		for(Direction directionCheck:Direction.values())
		{
			if(this.getBorderAt(directionCheck) instanceof Door && !directionCheck.equals(direction))
			{
				
				i++;
			}
		}
		if(i>=3)
		{
			return true;
		}
		else
			return false;
	}
	/**
	 * method to check if the square has 5 or more open borders
	 * @return true if there are to many open borders.
	 * 		| if( borders.Values() containes 5>= instanceof Open)
	 * 		| 	then( result == true)
	 */
	@Model
	protected boolean toManyOpenBorders(Direction direction)
	{
		int i = 0;
		
		for(Direction directionCheck:Direction.values())
		{
			if(getBorderAt(directionCheck) instanceof Open && !direction.equals(directionCheck))
				i++;
		}
		if(i>= 5)
		{
			return true;
		}
		else
			return false;
	}
	
	/**
	 * check if the given humidity percentage is valid for this square
	 * 
	 * @param humidity
	 * 		the value of the humidity that has to be checked.
	 * @return true only if the given humidity is valid
	 * 		| if( humidity >= 0 && humidity <= 100)
	 * 		| 	then result == true
	 * 		|else result == false
	 * @pre
	 * 		the given humidity has only two fraction numbers
	 * 		| hasTwoDecimals(humidity)
	 */
	public static boolean isValidHumidity(double humidity)
	{
		assert hasTwoDecimals(humidity);
		return (humidity >= 0 && humidity <= 100);
	}
	/**
	 * check if the given temperature is valid for this square
	 * 
	 * @param temperature
	 * 			the temperature that has to be checked
	 * @param type
	 * 			the temperatureType in which the temperature is expressed 
	 * @return true only if the given temperature is within the borders of the minimum and maximum temperature
	 * 			| if( getTemperature() >= getMinTemperatureInCelsius() && getTemperature() <= getMaxTemperatureInCelsius())
	 * 			| 	then result == true;
	 * 			| else	result == false;
	 * @throws IllegalTemperatureTypeException
	 * 			throws this exception if the temperaturetype is null
	 * 			| type == null
	 */
	public static boolean isValidTemperature(double temperature,TemperatureType type) throws IllegalTemperatureTypeException
	{
		return temperature >= getMinTemperature(type) && temperature <= getMaxTemperature(type);
			
	}
	/**
	 * set the humidity of this square
	 *  
	 * @param	 humidity
	 * 			the value of the humidity that has to be set.
	 * @pre 	 The given humidity must be valid
	 * 			|isValidHumidity(humidity)
	 * @pre		the given humidity has only 2 fraction numbers
	 * 			| hasTwoDecimals(humidity)
 	 * @post	the humidity of this square is between the given humidity of this square and
 	 * 			the himidity of the other squares in the space, or the given humidity if he is alone in the space
	 * 			|if(Dungeon.findSpace(square).size() = 1)
	 * 			| 	then(new.getHumidity() == this.getHumidity()
	 * 			|else if(humidity >= Dungeon.findSpace(square).get(1).getHumidity())
	 * 			|	then (new.gethumidity() =< humidity
	 * 			|		  && new.gethumidity() >= Dungeon.findSpace(square).get(1).getHumidity())
	 * 			|else( assert(humidity < Dungeon.findSpace(square).get(1).getHumidity())
	 * 			|	  && new.gethumidity() > humidity
	 * 			|	  && new.getHumidity() < Dungeon.findSpace(square).get(1).getHumidity())
	 */
	@Basic
	public void setHumidity(double humidity)
	{	

		assert isValidHumidity(humidity);
		assert hasTwoDecimals(humidity);
		this.humidity = humidity;
		adjustHumiditySpace();
	}
	/**
	 * 
	 * changes the humidity of a space of squares enclosed by walls or 
	 * closed doors to the weighted average.
	 * @post	the humidity of each square of the space is equal
	 * 			|for(Square spaceSquare : Dungeon.findSpace(square))
	 * 			|	for (Square otherSpaceSquare : Dungeon.findSpace(square)
	 * 			|		spaceSquare.getHumidity() = otherSpaceSquare.getHumidity()
	 * @post	the humidity of each square of the space is a valid humidity
	 *			|for (Square otherSpaceSquare : Dungeon.findSpace(square)
	 * 			|		IsValidHumidity(spaceSquare.getHumidity())
	 * @post	the humidity of this square is between the current value of this square and the himidity of the other squares in the space, or the given humidity if he is alone in the space
	 * 			|if(Dungeon.findSpace(square).size() = 1)
	 * 			| 	then(new.getHumidity() == this.getHumidity())
	 * 			|else if(square.this.getHumidity() >= Dungeon.findSpace(square).get(1).getHumidity())
	 * 			|	then (new.gethumidity() =< this.gethumidity()
	 * 			|		&& new.gethumidity() >= Dungeon.findSpace(square).get(1).getHumidity())
	 * 			|else( assert(square.this.getHumidity() < Dungeon.findSpace(square).get(1).getHumidity())
	 * 			| 		&&new.gethumidity() > this.getHumidity()
	 * 			|		&& new.getHumidity() < Dungeon.findSpace(square).get(1).getHumidity())
	 */
	public void adjustHumiditySpace()
	{
		HashSet<Square> space = Dungeon.findSpace(this).get("squares");
		int denominator = space.size();
		double totalhumidity= 0;
		for(Square spaceSquare : space)
		{
			totalhumidity+= spaceSquare.getHumidity();
		}
		double humidityPerSquare = totalhumidity/denominator; 
		for(Square spaceSquare : space)
		{
			spaceSquare.humidity = humidityPerSquare;
		}
	}
	/**
	 * returns the humidity of this square.
	 */
	@Basic
	@Raw
	public double getHumidity()
	{
		return humidity;
	}
	/**
	 * returns the temperature of this square in celsius
	 */
	@Basic
	@Raw
	public double getTemperature()
	{
	return temperature;	
	}
	/**
	 * returns the temperature of this square in the given temperatureType
	 * 
	 * @param 	type
	 * 			the temperatureType in which the temperature has to be returned
	 * @return
	 * 			the temperature in the wanted type.
	 * 			| if(type==TemperatureType.CELSIUS)
	 * 			| 	then (result == this.temperature)
	 * 			| else if (type== TemperatureException.FAHRENHEIT)
	 * 			| 	 then (result == temperature*1.8 + 32)
	 * 			| else(return == temperature + 273.15)
	 * @throws 	IllegalTemperatureTypeException
	 * 			throws this exception if the given temperature type is null
	 * 			| type == null
	 */
	public double getTemperature(TemperatureType type) throws IllegalTemperatureTypeException
	{
		if(type == null)
			throw new IllegalTemperatureTypeException("Invalid TemperatureType");
		if(type==TemperatureType.CELSIUS)
			return temperature;
		else if(type == TemperatureType.FAHRENHEIT)
			return temperature*1.8 + 32;
		else
		{
			assert(type == TemperatureType.KELVIN);
			return temperature + 273.15;
		}
	}
	/**
	 * returns the minimum temperature of the square class in celsius
	 */
	@Basic
	public static double getMinTemperature()
	{
		return MIN_TEMPERATURE;
	}
	/**
	 * returns the maximum temperature of the square class in celsius
	 */	
	@Basic
	public static double getMaxTemperature()
	{
		return MAX_TEMPERATURE;
	}
	/**
	 *  returns the minimum temperature in the given temperature type
	 *  	
	 * @param 	type
	 * 			the temperature type in which the minimum temperature of the square class has to be returned
	 * @throws 	IllegalTemperatureTypeException 
	 * 			throws this exception if the given temperature type is null
	 * 			| type == null
	 */
	public static double getMinTemperature(TemperatureType type) throws  IllegalTemperatureTypeException
	{
		if(type == null)
			throw new IllegalTemperatureTypeException("invalid TemperatureType");
		else if(type == TemperatureType.CELSIUS)
			return MIN_TEMPERATURE;
		else if(type == TemperatureType.FAHRENHEIT)
			return MIN_TEMPERATURE*1.8 + 32;
		else
		{
			assert(type == TemperatureType.KELVIN);
			return MIN_TEMPERATURE + 273.15;
		}
	}
	/**
	 *  returns the maximum temperature in the given temperature type
	 *  
	 * @param 	type
	 * 			the temperature type in which the maximum temperature of the square class has to be returned
	 * @return 	The maximum temperature in the given temperature type 
	 * @throws 	IllegalArgumentException
	 * 			throws this exception if the given temperature type is null
	 * 			| type == null		
	 */
	public static  double getMaxTemperature(TemperatureType type) throws IllegalTemperatureTypeException
	{
		if(type == null)
			throw new IllegalTemperatureTypeException("invalid temperature type");
		else if(type == TemperatureType.CELSIUS)
			return MAX_TEMPERATURE;
		else if(type == TemperatureType.FAHRENHEIT)
			return MAX_TEMPERATURE*1.8 + 32;
		else
			return (MAX_TEMPERATURE + 273.15);
		}

	/**
	 * sets the minimum temperature of the square to the given temperature, taking in account the given
	 * 	temperature type of the temperature
	 * 
	 * @param 	minTemperature
	 * 			the new minimum temperature in the given temperature type
	 * @param 	type
	 * 			the temperature type of the given temperature
	 * @post	the new minTemperature of this square is equal to the given minTemperature
	 * 			| this.getMinTemperatureIn(type) == minTemperature;
	 * @throws 	IllegalTemperatureTypeException
	 * 			throws this exception if the given type == null
	 * 			| type == null
	 * @throws 	IllegalArgumentException
	 * 			throws this exception if the given minimum temperature is not valid for the square class
	 * 			| !isValidMinTemperature(minTemperature,type);
	 */
	public static void setMinTemperature(double minTemperature,TemperatureType type) throws IllegalArgumentException, IllegalTemperatureTypeException
	{
		if(!isValidMinTemperature(minTemperature,type))
			throw new IllegalArgumentException("invalid min temperature");
		if(type == TemperatureType.CELSIUS)
			Square.MIN_TEMPERATURE = minTemperature;
		else if (type == TemperatureType.FAHRENHEIT)
			Square.MIN_TEMPERATURE = (minTemperature-32)/1.8;
		else if(type == TemperatureType.KELVIN)
			Square.MIN_TEMPERATURE = minTemperature - 273.15;
		else
			throw new IllegalTemperatureTypeException("invalid temperature type");
	}
	/**
	 *sets the maximum temperature of the square to the given temperature, taking in account the given
	 * 	temperature type of the temperature
	 * 
	 * @param 	maxTemperature
	 * 			the new maximum temperature in the given temperature type
	 * @param 	type
	 * 			the temperature type of the given temperature
	 * @post	the new maxTemperature of this square is equal to the given maxTemperature
	 * 			| this.getMaxTemperatureIn(type) == maxTemperature
	 * @throws 	IllegalTemperatureTypeException
	 * 			throws this exception if the given type == null
	 * 			| type == null
	 * @throws 	IllegalArgumentException
	 * 			throws this exception if the given maximum temperature is not valid for the square class
	 * 			| !isValidMaxTemperature(maxTemperature,type)
	 */
	public static void setMaxTemperature(double maxTemperature,TemperatureType type) throws IllegalArgumentException, IllegalTemperatureTypeException
	{
		if(!isValidMaxTemperature(maxTemperature,type))
			throw new IllegalArgumentException("invalid max temperature");
		if(type == TemperatureType.CELSIUS)
			Square.MAX_TEMPERATURE = maxTemperature;
		else if (type == TemperatureType.FAHRENHEIT)
			Square.MAX_TEMPERATURE = (maxTemperature-32)/1.8;
		else if (type == TemperatureType.KELVIN)
			Square.MAX_TEMPERATURE = maxTemperature - 273.15;
		else
			throw new IllegalTemperatureTypeException("invalid temperature type");
	}
	/**
	 * checks if the given minimum temperature is valid as a minimum temperature
	 * depending on the given temperature type
	 * 
	 * @param 	minTemperature
	 * 			the minimum temperature that has to be checked
	 * @param 	type
	 * 			the temperature type in which the temperature is expressed 
	 * @return	if the given minimum temperature is valid for this square 
	 * @throws 	IllegalTemperatureTypeException
	 * 			throws this exception if the temperature type is null
	 * 			| type == null
	 */
	public static boolean isValidMinTemperature(double minTemperature,TemperatureType type) throws IllegalTemperatureTypeException
	{
		if(type == null)
		{
			throw new IllegalTemperatureTypeException("invalid temperature type");
		}
		if(minTemperature >= Square.getMaxTemperature(type))
		{
			return false;
		}
		else if(type == TemperatureType.CELSIUS)
		{
				if (minTemperature < 0)
					return (minTemperature >= -Double.MAX_VALUE / 1.8);
				else
					return true;
		}
		else if(type == TemperatureType.FAHRENHEIT)
		{
				return minTemperature >= -Double.MAX_VALUE - 32;
		}
		else 
		{	
			assert(type == TemperatureType.KELVIN);
			if( minTemperature < 0)
					return ((minTemperature - 273.15)) >= -Double.MAX_VALUE / 1.8 && minTemperature >= (-Double.MAX_VALUE + 273.15);
				else
					return true;
		}
	}
	/**
	 * checks if the given maximum temperature is valid as a maximum temperature
	 * 
	 * @param 	maxTemperature
	 * 			the new maximum temperature of this square
	 * @param 	type
	 * 			the temperature type in which this temperature is expressed
	 * @return	true if the given maximum temperature is valid for the square class, false if not.
	 * @throws 	IllegalTemperatureTypeException 
	 * 			throws this exception if the given type is null
	 * 			| type == null
	 */
	public static boolean isValidMaxTemperature(double maxTemperature,TemperatureType type) throws IllegalTemperatureTypeException
	{
		if(maxTemperature <= Square.getMinTemperature(type))
		{
			return false;
		}
		if(type == TemperatureType.CELSIUS)
		{
			if((maxTemperature <= Double.MAX_VALUE / 1.8))
				return Math.abs(maxTemperature*1.8) <= Double.MAX_VALUE - 32;
			else
				return false;
		}
		if(type == TemperatureType.FAHRENHEIT)
		{
			return true;
		}
		else
		{
			assert(type == TemperatureType.KELVIN);
			if((maxTemperature- 273.15 <= Double.MAX_VALUE / 1.8))
				return maxTemperature*1.8 <= Double.MAX_VALUE - 32;
			else
				return false;
		}
		
	}
	
	/**
	 * returns the cold damage delivered by the square caused by a low temperature
	 * 
	 * @post	the return of this method should always be bigger then or equal to zero
	 * 			| return >= 0
	 * @return
	 * 			The value of the cold damage inflicted, 1 point for every
	 * 			10 degrees under -5 degrees, and zero if above -5 degrees.
	 * 			|if(temperatureLowEnoughForColdDamage(temperature))
	 * 			| 	then (result == (-temperature -5)/10)
	 * 			|else result == 0
	 * @note	the method temperatureLowEnoughForColdDamage() checks if the given temperature 
	 * 			is under -5 degrees celsius so the absolute value - 5 >= 0	
	 */
	public int coldDamage() 
	{
		if(temperatureLowEnoughForColdDamage())
			return (int)(Math.abs(getTemperature()) -5)/10;
		return 0;
	}
	/**
	 * 	returns the heat damage delivered by the square caused by a high temperature
	 * 
	 * @post	the value of the heat damage should always be bigger then or equal to zero
	 * 			| return >= 0
	 * @return	the value of the heat damage inflicted is variable and standard set on 1
	 * 			point for every 15 degrees above 35 degrees , and zero under 35 degrees
	 *			|if(temperatureHighEnoughForHeatDamage(temperature))
	 * 			| 	then (result == ((this.getTemperature() - this.getStartTemperatureHeatDamage())
	 * 			|						/this.getTemperatureStepHeatDamage())
	 * 			|else result == 0 
	 * @Throws IllegalArgumentException
	 * 			Throws this exception if the damage is outside the range of double
	 * 			| damage > Double.MAX_VALUE
	 * @note	no exceptions can be thrown 
	 *
	 */	
	public int heatDamage() throws IllegalArgumentException
	{
		if(temperatureHighEnoughForHeatDamage())
		{
			if(getTemperature() >= Double.MAX_VALUE -Math.abs(getStartTemperatureHeatDamage()) 
					&& getStartTemperatureHeatDamage() < 0 
					|| getTemperature() - getStartTemperatureHeatDamage() >= Double.MAX_VALUE*getTemperatureStepHeatDamage()
					&& getTemperatureStepHeatDamage() < 1)
				throw new IllegalArgumentException("to large number");
			else
				return (int) ((getTemperature() - getStartTemperatureHeatDamage())/getTemperatureStepHeatDamage());
		}
		return 0;
	}
	
	/**
	 * checks if the temperature of this square is low enough to inflict cold damage
	 * 

	 * @return	true if the temperature is low enough
	 * 		 	|result == temperature <= -5
	 * @note	no exceptions can be thrown, double is a primitive type
	 */
	public boolean temperatureLowEnoughForColdDamage()
	{
		return getTemperature() <=-5;
	}
	/**
	 * returns the rust damage delivered by the square caused by a high humidity
	 * 
	 *@return	the rust damage value inflicted, 1 point ever 7 percent above 30 percent, zero below
	 * 			|if(!rustPossible(getHumidity())
	 * 			|	then return 0
	 * 			|else	(result == (int) (getHumidity() - 30)/ 7)
	 * @post	the value of the rust damage should always be bigger then or equal to zero
	 * 			| return >= 0
	 * @note	no pre conditions
	 */
	public int RustDamage()
	{
		if(rustPossible())
		{
			return (int) (getHumidity() - 30)/7;
		}
		return 0;
	}
	/**
	 * checks if the humidity of this square is high enough to inflict rust damage
	 * 
	 * @pre		the given humidity must be valid
	 * 			| isValidHumidity(getHumidity())
	 * @param 	humidity
	 * 			the humidity that has to be checked
	 * @return	true if the humidity of this square is high enough
	 * 		 	|result == getHumidity() >= 30
	 */
	public boolean rustPossible()
	{
		return getHumidity() >= 30;
	}
	/**
	 * checks if the given temperature is high enough to inflict heat damage
	 * 
	 * @return	true if the temperature of this square is high enough for heat damage
	 * 			|result == getTemperature() >= getStartTemperatureHeatDamage()
	 * @note	no exceptions thrown, double is an primitive type
	 */
	public boolean temperatureHighEnoughForHeatDamage()
	{
		return getTemperature() >= getStartTemperatureHeatDamage();
	}
	/**
	 * returns the inhabitability of a square which is an attribute depending on 
	 * cold damage, heat damage and humidity and gives the player a first impression 
	 * of the damage a square could inflict 
	 * 
	 * @pre		the summation -Math.sqrt((heatDamage())^3)/Math.sqrt(101-getHumidity())-Math.sqrt(coldDamage())
	 * 			must be smaller then  the double range, as well as all calculations done in between.
	 * 			|Math.abs(-Math.sqrt((heatDamage())^3)/Math.sqrt(101-getHumidity())
	 * 			|	-Math.sqrt(coldDamage())) < Double.MAX_VALUE
	 * 			|&& Math.abs(-Math.sqrt((heatDamage())^3)) < Double.MAX_VALUE
	 * 			|&& Math.abs(Math.sqrt(101-getHumidity())) < Double.MAX_VALUE
	 * 			|&& Math.abs(Math.sqrt(coldDamage())) < Double.MAX_VALUE
	 * @return	the inhabitability of a square depending on the given heat damage,
	 * 			cold damage and  humidity
	 * 			|result == -Math.sqrt((heatDamage())^3)/Math.sqrt(101-getHumidity())-Math.sqrt(coldDamage())
	 * @post	the value of the rust damage should always be negative or zero
	 * 			| result <= 0
	 */
	public double inhabitability ()
	{
		double heat = heatDamage();
		double cold = coldDamage();
		if(heat == 0 && cold == 0)
		{
			return 0;
		}
		else if(heat == 0)
		{
			return -1*Math.sqrt(cold);
		}
		else if(cold == 0)
		{
			return -1*((heat*Math.sqrt(heat))/Math.sqrt(101-getHumidity()));
		}
		
		else
		{
		return -1*((heat*Math.sqrt(heat))/Math.sqrt(101-getHumidity())-Math.sqrt(cold));
		}
	} 
	/**
	 * returns the temperature that is the minimum temperature from where heat damage is possible
	 * 
	 * @return 	the temperature  that is the minimum temperature from where heat damage is possible
	 * 			| result == this.startTemperatureHeatDamage
 	 */
	@Basic
	@Raw
	public static double getStartTemperatureHeatDamage() {
		return startTemperatureHeatDamage;
	}
	/**
	 * sets the start the temperature that is the minimum temperature from where heat damage is possible
	 * 
	 * @param 	startTemperatureHeatDamage
	 * 			the minimum temperature from where heat damage is possible
	 * @param 	type
	 * 			the temperature that is the minimum temperature from where heat damage is possible
	 * @throws 	IllegalTemperatureTypeException
	 * 			this exception is thrown when there is a invalid temperature type given
	 * 			|type == null
	 * @throws 	IllegalArgumentException
	 * 			case one: when the maximum value of a Double is made negative and augmented
	 * 				with 32 exceeds the temperature
	 * 				|temperature <= -Double.MAX_VALUE + 32
	 * 			case two: when the temperature subtracted with 32 and made positive exceeds 
	 * 				the maximum value of a Double divided by 1.8
	 * 				|Math.abs(temperature -32) >= Double.MAX_VALUE/1.8
	 * 			case three: when the maximum value of a Double is made negative and augmented
	 * 				with 273.15 exceeds the temperature
	 * 				|temperature <= -Double.MAX_VALUE + 273.15
	 * @post	the given startTemperature of the heat damage is the startTemperature of this square 
	 * 			|new.getStartTemperatureHeatDamage() == startTemperatureHeatDamage
	 * @note	the value of the startTemperature of the heat damage could be outside the boundaries of
	 * 			the minimum and the maximum temperature if an absence of heat damage is preferred
	 */
	@Basic
	public static void setStartTemperatureHeatDamage(double startTemperatureHeatDamage,TemperatureType type) throws IllegalArgumentException,IllegalTemperatureTypeException
	{
		
		Square.startTemperatureHeatDamage = getTemperatureInCelsius(startTemperatureHeatDamage,type);
	}
	/**
	 * returns the heat damage temperature step which is the increasing step for damage points
	 * starting from the startTemperatureHeatDamage-temperature
	 * 
	 * @return 	the heat damage temperature step
	 * 			| result == this.temperatureStepHeatDamage
	 */
	@Basic
	public static double getTemperatureStepHeatDamage() {
		return temperatureStepHeatDamage;
	}
	/**
	 * sets the heat damage temperature step  which is the increasing step for damage points
	 * starting from the startTemperatureHeatDamage-temperature
	 * 
	 * @param 	temperatureStepHeatDamage
	 * 			the heat damage temperature step  which is the increasing step for damage points
	 * 			starting from the startTemperatureHeatDamage-temperature
	 * @post	the heat damage temperature step is not negative
	 * 			 |getTemperatureStepHeatDamage()>=0
	 * @post	the given heat damage temperature step is the heat damage temperature step of this square
	 * 			| new.getTemperatureStepHeatDamage() == temperatureStepHeatDamage
	 * @throws 	IllegalArgumentException
	 * 			throws this exception if the temperatureStepHeatDamage is negative
	 * 			| getTemperatureStepHeatDamage() <=0
	 */

	public static void setTemperatureStepHeatDamage(double temperatureStepHeatDamage) throws IllegalArgumentException {
		if(temperatureStepHeatDamage <= 0)
			throw new IllegalArgumentException("invalid Step for heat damage");
		Square.temperatureStepHeatDamage = temperatureStepHeatDamage;
	}

	/**
	 * returns the boolean if the square is slippery. if the square satisfies the three conditions
	 * an attribute (boolean) of square
	 * a humidity of 100% and a temperature above zero
	 * a humidity greater then 10% and a temperature under zero
	 * true if conditions are fulfilled and the square is slippery
	 * @return 	true when the humidity is 100 percent and the temperature is above zero
	 * 			|if(getHumidity() == 100 && getTemperature() > 0
	 * 			|	then result == true
	 * 			true when the humidity is above 10 percent
	 * 			and the temperature is under or equal to zero
	 * 			|if(getHumidity() > 10 && getTemperature() <= 0)
	 * 			|	then true
	 * 			false when conditions not fulfilled
	 * 			|else return false
	 */
	public boolean slippery()
	{
		if (getHumidity() == 100 && getTemperature() > 0)
		{
			return true;
		}
		else if (getHumidity() > 10 && getTemperature() <= 0 )
		{
			return true;
		}
		else if (slipperyFloor == true )
		{
			return true;
		}
		else	
		{
			return false;
		}
	}
	
	//TODO volgens mij mag deze weg, op eerste zicht nergens gebruikt
	/**
	 * @return true when the temperature is higher or equal to 500
	 * 			|result == (getTemperature() >= 500)
	 */
	public boolean TemperatureHigherThen500()
	{	
		if(getTemperature() < 500)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	/**
	 * Checks if the humidity of a square is 0%
	 * 
	 * @param 	square
	 * 			the definition of the smallest unit of a location with attributes
	 * 			temperature, humidity and slippery floor 
	 * @return	true if the humidity of a square is 0%
	 * 			|result == (square.getHumidity() == 0)
	 * @note	no exceptions thrown
	 */
	public static boolean isDry(Square square)
	{
		return square.getHumidity() == 0;
	}
	
	 
	/**
	 * transforms the given temperature to the temperature type Celsius according to the given temperature type
	 * 
	 * @param	type
	 * 			the temperatureType in which the temperature is expressed
	 * @param 	Temperature
	 * 			the given temperature in the given temperature type
	 * @throws	IllegalArgumentException
	 * 			this exception is thrown when the value of the given temperature exceeds the borders
	 * 			of the minimum and maximum temperature
	 * 			|!isValidTemperature(temperature,type)
	 * @throws 	IllegalTemperatureTypeException 
	 * 			this exception is thrown when there is a invalid temperature type given
	 * 			|type == null
	 * @post	the given temperature is equal to the temperature of this square in the unit of the given temperature.
	 * 			|new.getTemperature(type) == temperature
	 */	
	public void setTemperatureToCelsius(TemperatureType type,double temperature) throws IllegalArgumentException,InvalidTemperatureException, IllegalTemperatureTypeException
	{
		if(type == null)
		{
			throw new IllegalTemperatureTypeException("invalid temperatureType");
		}
		if(!isValidTemperature(temperature,type))
		{
			throw new InvalidTemperatureException(temperature);
		}
		if(type == TemperatureType.CELSIUS)
		{
			setTemperature(temperature);
		}
		else if(type == TemperatureType.KELVIN)
		{
			setTemperature(temperature -273.15);
		}
		else
		{
			assert(type == TemperatureType.FAHRENHEIT);
			setTemperature((temperature-32)/1.8);
		}
	}
	/**
	 * returns the temperatureMergeConstant which is a constant used in the 
	 * calculation of the temperature when squares are merged
	 * 
	 * @return	the variable temperatureMergeConstant
	 */
	public static double getTemperatureMergeConstant()
	{
		return temperatureMergeConstant;
	}
	/**
	 * setter that sets the variable TemperatureMergeConstant and checks if it is valid
	 * 
	 * @param 	constant
	 * 			a constant used in the calculation of the temperature when squares are merged
	 * 			its value lies between 0.1 and 0.4
	 * @throws  IllegalArgumentException
	 * 			this exception is thrown when temperatureMergeConstant is invalid
	 * 			| !isValidTemperatureMergeConstant(constant)
	 */ 
	public static void setTemperatureMergeConstant(double constant) throws IllegalArgumentException
	{
		if(isValidTemperatureMergeConstant(constant))
		{
			Square.temperatureMergeConstant = constant;
		}
		else
			throw new IllegalArgumentException("invalid constant");
	}
	/**
	 * checks if the variable TemperatureMergeConstant is valid
	 * 
	 * @param 	constant
	 * 			a constant used in the calculation of the temperature when squares are merged
	 * 			its value lies between 0.1 and 0.4
	 * @return 	true if the constant its value lies between 0.1 and 0.4
	 * 			|constant >= 0.1 && constant <= 0.4
	 */
	public static boolean isValidTemperatureMergeConstant(double constant)
	{
		return constant >= 0.1 && constant <= 0.4;
	}
	/**
	 * @return returns if the floor of the square is slippery as an attribute 
	 * 			of the floor (slippery stones etc.)
	 */
	@Basic
	public boolean getSlipperyFloor()
	{
		return slipperyFloor;
	}
	/**
	 * sets the boolean slipperyFloor
	 * 
	 * @param 	slipperyFloor
	 * 			an attribute a square which indicates if the floor of a square is slippery 
	 * 			independent of temperature and humidity (slippery stones etc.)
	 */
	public void setSlipperyFloor(boolean slipperyFloor)
	{
		this.slipperyFloor = slipperyFloor;
	}
	/**
	 * transforms the given temperature to the temperature type Celsius according to the given temperature type
	 * 
	 * @param 	temperature
	 * 			the given temperature in the given temperature type
	 * @param 	type
	 * 			the type of the given temperature (fahrenheit,kelvin or celsius) that is to be converted to celsius
	 * @return	
	 * @throws 	IllegalTemperatureTypeException
	 * 			this exception is thrown when there is a invalid temperature type given
	 * 			|type == null
	 * @throws 	IllegalArgumentException
	 * 			case one: when the maximum value of a Double is made negative and augmented
	 * 				with 32 exceeds the temperature
	 * 				|temperature <= -Double.MAX_VALUE + 32
	 * 			case two: when the temperature subtracted with 32 and made positive exceeds 
	 * 				the maximum value of a Double divided by 1.8
	 * 				|Math.abs(temperature -32) >= Double.MAX_VALUE/1.8
	 * 			case three: when the maximum value of a Double is made negative and augmented
	 * 				with 273.15 exceeds the temperature
	 * 				|temperature <= -Double.MAX_VALUE + 273.15
	 */
	public static double getTemperatureInCelsius(double temperature, TemperatureType type) throws IllegalTemperatureTypeException,IllegalArgumentException
	{
		if(type == TemperatureType.CELSIUS)
		{
			return temperature;
		}
		else if(type == TemperatureType.FAHRENHEIT)
		{
			if(temperature <= -Double.MAX_VALUE + 32)
				throw new IllegalArgumentException("invalid temperature, overflow");
			if(Math.abs(temperature -32) >= Double.MAX_VALUE/1.8)
				throw new IllegalArgumentException("invalid temperature, overflow");
			return (temperature-32)/1.8;
		}
		else if(type == TemperatureType.KELVIN)
		{
			if(temperature <= -Double.MAX_VALUE + 273.15)
				throw new IllegalArgumentException("invalid temperature");
			return temperature - 273.15;
		}
		else
			throw new IllegalTemperatureTypeException("invalid temperature type");
	}
	/**
	 * this method returns the opposite direction of the given direction(example: north-> south, ceiling->floor...), used when changing borders
	 * @param direction
	 * 			the direction of which we want to find the opposite direction.
	 * @return
	 * 		the opposite direction of the given direction. this will never be the same as the given
	 * 
	 */
	public static Direction getOppositeDirection(Direction direction)
	{
		if(direction == Direction.CEILING)
			return Direction.FLOOR;
		else if(direction == Direction.FLOOR)
			return Direction.CEILING;
		else if(direction == Direction.NORTH)
			return Direction.SOUTH;
		else if(direction == Direction.EAST)
			return Direction.WEST;
		else if(direction == Direction.SOUTH)
			return Direction.NORTH;
		else if(direction == Direction.WEST)
			return Direction.EAST;
		else
			return null;
	}
	/**
	 * changes the state of door in the given direction if there is a door is that direction
	 * @param direction
	 * 			the direction in which you want the door to change.
	 * @post if the door was opened, the humidity and temperature of the squares in the space are the same.
	 * 		|if(Dungeon.findSpace(this).size() != null)
	 * 		|	then (new.getTemperature() == Dungeon.findSpace(this).toArray().get(1).new.getTemperature() (!= this)
	 * 		|		&& new.getHumidity() == Dungeon.findSpace(this).toArray().get(1).new.gethumidity() (!= this))
	 * 		| else(new.getTemperature() == this.getTemperature()
	 * 		| 		&&new.getHumidity() == this.getHumidity())
	 */
	public void changeStateOfDoor(Direction direction)
	{
		if(getBorderAt(direction) instanceof Door)
		{
			if(((Door)getBorderAt(direction)).getIsOpen())
			{
				((Door)getBorderAt(direction)).setIsOpen(false);
			}
			else
				((Door)getBorderAt(direction)).setIsOpen(true);
				adjustTemperatureSpace();
				adjustHumiditySpace();
		}
	}
	/**
	 * removes all border references for this square. used by dungeon to completely remove a square
	 * @param 	square
	 * 			the square of which the borders have to be removed
	 * @param 	x
	 * 			the x coordinate of the location of the given square in the dungeon
	 * @param 	y
	 * 			the y coordinate of the location of the given square in the dungeon
	 * @param 	z
	 * 			the z coordinate of the location of the given square in the dungeon
	 * @param 	dungeon
	 * 			the dungeon in which the given square should be located.
	 * @post 	if the square was equal to the square on the given location of the dungeon, its borders hashmap is empty
	 * 			| square.new.getBorders().isEmpty()
	 * @note 	the coordinates functions as a private key since there is no method in dungeon
	 * 			that returns these coordinates and a square can't know his coordinates. Thus if
	 * 			the given coordinates matches the given square this method is called from Dungeon.
	 * @note	After the reference of the borders to the square is deleted, the reference of
	 * 			the square to the borders is deleted, which temporarily violates the class invariant
	 * 			but with no references the square is deleted.
	 */
	@Raw
	public static void deleteSquare(Square square,long x, long y, long z, Dungeon<Square> dungeon)
	{
		Boolean deleteRight = dungeon.getSquareAt(x, y, z).equals(square);
		Boolean notAllowed = true;
		if(deleteRight)
		{
			int i = 0;
			for(Direction direction: Direction.values())
			{
				notAllowed = (square.getBorderAt(direction).getAttachedSquare1().equals(square)
				|| square.getBorderAt(direction).getAttachedSquare1().equals(square));
				if (!notAllowed)
				{
					i++;
				}
			
			}
			if(i == 6)
			{
				square.borders.clear();
				square.isTerminated = true;
				Runtime r = Runtime.getRuntime();
				r.gc();
			}			
		}
	}
	/**
	 * 
	 * @return
	 * 		| result = this.isTerminated
	 */
	public boolean isTerminated()
	{
		return this.isTerminated;
	}
	
	/**
	 * adds a new teleportable square to the list
	 * @param 	square
	 * 			the new square you want to add
	 * @pre		the square may not be already present in the list
	 * 			|!this.teleport.getTeleportableSquaresList().contains(square)
	 * @post 	the square is added and is now teleportable
	 * 			| new.Teleportation.teleportableSquares.contains(square)
	 */
	public void addTeleportableSquare(Square square)
	{
		this.teleport.addTeleportableSquare(square);
	}
	/**
	 * deletes the given square as a teleportation possibility
	 * @param square
	 * 			the square you want to add
	 * @pre
	 * 		the square must exist in the list.
	 * 		|this.teleport.teleportableSquares.contains(square)
	 * @post
	 * 		the square is deleted
	 * 		| !new.teleport.teleportableSquares.contains(square)
	 */
	public void removeTeleportableSquare(Square square)
	{
		this.teleport.removeTeleportableSquare(square);
	}
	/**
	 * changes the state of the possibility to teleport.
	 * @param possible
	 * 		the state you want to change it to.
	 * @post
	 * 		the new state equals the given state.
	 * 		| new.teleport.teleportPossible() == possible
	 */
	public void setTeleportationPossible(boolean possible)
	{
		this.teleport.setIfTeleportPossible(possible);
	}
	/**
	 * 
	 * @return
	 * 		a randomly selected square from the teleportable list of this square.
	 */
	public Square teleport()
	{
		return this.teleport.getRandomTeleportableSquare();
	}
	/**
	 * 
	 * @return
	 * 		true if you can teleport to another square from this square.
	 */
	public boolean teleportpossible()
	{
		return this.teleport.teleportPossible();
	}
	/**
	 * 
	 * @return
	 * 		a copy of the list containing all the teleportable squares.
	 */
	public ArrayList<Square> getTeleportableSquares()
	{
		return teleport.getTeleportableSquares();
	}
	/**
	 * @return 	true when the temperature of a square is higher then a given double
	 * 			|result == (this.getTemperature() >= temperature)
	 */
	@Override
	public boolean TemperatureHigherOrEqualTo(double temperature) {
		if(this.getTemperature() >= temperature)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	/**
	 *@return true when the temperature of a square is higher then a given double
	 * 			|result == (this.getTemperature() >= temperature)
	 */
	@Override
	public boolean TemperatureLowerOrEqualTo(double temperature) {
		if(this.getTemperature() >= temperature)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	/**
	 * @return 	true when the square can teleport to the given square
	 * 			|result == (this.getTeleportableSquares().contains(square))
	 */
	@Override
	public boolean TeleportTo(Square square) {
		if(this.teleportpossible())
		{
			if(this.getTeleportableSquares().contains(square))
				return true;
		}
		return false;
	}
	/**
	 * @return	true when the square is a rock
	 * 			|result == ( this instanceof RockSquare)
	 */
	@Override
	public boolean isRockSquare() {
		if( this instanceof RockSquare)
			return true;
		else
			return false;
	}
	/**
	 * @return	true when the square is a transparant one
	 * 			|result == (this instanceof TransparentSquare)
	 */
	@Override
	public boolean isTransparentSquare() {
		if(this instanceof TransparentSquare)
			return true;
		else
			return false;
	}
	/**
	 * @return	true when the humidity is equal or lower then the given double
	 * 			|result == (this.getHumidity() <= humidity)
	 */
	@Override
	public boolean humidityLowerOrEqualTo(double humidity) {
		
		if(this.getHumidity() <= humidity)
			return true;
		else
			return false;
	}
	/**
	 * @return	true when the humidity is equal or higher then the given double
	 * 			|result == (this.getHumidity() >= humidity)
	 */
	@Override
	public boolean humidityHigherOrEqualTo(double humidity) {
		if(this.getHumidity() >= humidity)
			return true;
		else
			return false;
	}
}

